//! \file MlpAgent.h
//! \brief Defines the <code>fullsail_ai::MlpAgent</code> class.
//! \author Cromwell D. Enage
#ifndef _FULLSAIL_AI_MLP_AGENT_H_
#define _FULLSAIL_AI_MLP_AGENT_H_

#include <vector>
#include "MlpNetwork.h"

namespace fullsail_ai {

	//! \brief Simplistic user agent of a neural network.
	//!
	//! This is the highest-level class in the core containment hierarchy for this lab.  All user
	//! code must work with this interface only in order to access and/or modify the underlying
	//! neural network.  In turn, all operations that this class provides must work with the
	//! <code>MlpNetwork</code> interface in order to access and/or modify the underlying weights
	//! and biases.
	//!
	//! Sample usage:
	//!  -# Initialize the agent.
	//!  -# During load time, train the agent until it has learned some specific training sets.
	//!  -# During game time, update the agent's current action as needed.
	class MlpAgent
	{
		MlpNetwork          network;
		std::vector<double> currentOutputs;
		std::size_t         currentActionIndex;

	public:
		//! \brief Returns an unmodifiable view of this agent's network.
		//!
		//! \return  A <code>const</code> view of the network.
		inline MlpNetwork const& getNetwork() const
		{
			return network;
		}

		//! \brief Returns the current output values of this agent's network.
		//!
		//! \return  The network's current output values.
		inline std::vector<double> const& getCurrentOutputs() const
		{
			return currentOutputs;
		}

		//! \brief Returns the index of the highest-value element in the container of output
		//! values.
		//!
		//! \return  The index of the highest output value.
		inline std::size_t getCurrentActionIndex() const
		{
			return currentActionIndex;
		}

		//! \brief Initializes this agent.
		//!
		//! \param   numberGenerator    a zero-argument function that returns a different integer
		//!                             each time it is invoked.
		//! \param   inputCount         the number of inputs that this agent must take into
		//!                             consideration.
		//! \param   hiddenNeuronCount  the number of hidden neurons that the network will hold.
		//! \param   actionCount        the number of possible actions that this agent may
		//!                             undertake.
		//!
		//! \pre
		//!   - The range of \a numberGenerator is <code>[0, RAND_MAX]</code>.
		//!   - <code>1 \<</code> \a inputCount
		//!   - <code>1 \<</code> \a hiddenNeuronCount
		//!   - <code>1 \<</code> \a actionCount
		void initialize(int (*numberGenerator)(), std::size_t inputCount,
		                std::size_t hiddenNeuronCount, std::size_t actionCount);

		//! \brief Trains this agent's network to learn the specified training sets.
		//!
		//! \param   trainingSetInputs  a container of sample input value containers.
		//! \param   expectedOutputs    the container of corresponding values that the
		//!                             <code>MlpNetwork::feedForward()</code> method should
		//!                             output.
		//! \param   learningFactor     the rate at which the network makes progress in learning.
		//!
		//! \pre
		//!   - The agent's network was previously initialized.
		//!   - All elements in \a trainingSetInputs are the same size, which was the input count
		//!     used to initialize this agent's network.
		//!   - All elements in \a expectedOutputs are the same size, which was the action count
		//!     used to initialize this agent's network.
		//!   - The size of \a trainingSetInputs is nonzero.
		//!   - There are as many training set inputs as there are expected outputs.
		//!   - For each output container in \a expectedOutputs, exactly one element has a value
		//!     of one; all other elements have a value of zero.
		//!   - <code>0 \<</code> \a learningFactor
		void train(std::vector<std::vector<double> > const& trainingSetInputs,
		           std::vector<std::vector<double> > const& expectedOutputs,
		           double learningFactor);

		//! \brief Calculates the index of the highest-value element in the output container.
		//!
		//! \param   outputs  the output container.
		//! \return  The index of the maximum output element.
		//!
		//! \pre
		//!   - The size of \a outputs is nonzero.
		static std::size_t computeIndexOfMaxOutput(std::vector<double> const& outputs);

		//! \brief Calculates the index of the current action of this agent as well as the outputs
		//! of its network.
		//!
		//! \param   inputs   the container of input values that this agent's network will
		//!                   evaluate.
		void update(std::vector<double> const& inputs);
	};
}  // namespace fullsail_ai

#endif  // _FULLSAIL_AI_MLP_AGENT_H_

